Scenario #9680: An Endpoint Scoped Api Key Cannot Use Business Apis

An API-key restricted to the endpoint-scope rbac.subjects:sync cannot use any business API, here GET /api/hs/office/memberships as an example. Even though the global ADMIN role is granted to its API_KEY subject, which would allow viewing all memberships, the endpoint is out of scope and responds with 403 Forbidden.

Properties

Given

name value
subjectUuid a91c0008-0000-0000-0000-000000000008
subjectName business.attempt.key

An API-key can be restricted to named endpoint-scopes, here rbac.subjects:sync, which only allows GET /api/rbac/subjects, GET /api/rbac/subjects/{uuid}, and PUT /api/rbac/subjects/{uuid} (create-or-update, incl. declarative deactivation via deactivated: true — deliberately no DELETE). The scopes are an additional fence on top of the roles granted to the API_KEY subject: even with the global ADMIN role, e.g. granted to sync ALL subjects as needed for a Keycloak subject synchronization, all endpoints outside the scopes respond with 403 Forbidden. An API-key without scopes remains unrestricted.

Create the endpoint-scoped API_KEY Subject

The scopes property restricts the API-key to the given named endpoint-scopes. The response contains the generated clear-text API-key (property apiKey) exactly once; it cannot be retrieved again.

HTTP POST "/api/rbac/subjects" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  <<EOF
{
  "uuid" : "a91c0008-0000-0000-0000-000000000008",
  "name" : "business.attempt.key",
  "type" : "API_KEY",
  "scopes" : [ "rbac.subjects:sync" ]
}
EOF
=> status: 201 CREATED a91c0008-0000-0000-0000-000000000008
{
  "uuid" : "a91c0008-0000-0000-0000-000000000008",
  "name" : "business.attempt.key",
  "organization" : "business",
  "type" : "API_KEY",
  "apiKey" : "hsak_business.attempt.key.70ec544ed2d4374d8a00e88858c581d983ac2c19d4fc2eee1740bd759c3782c8",
  "scopes" : [ "rbac.subjects:sync" ],
  "expiresAt" : null
}

Prerequisite: Resolve the UUID of the global ADMIN role

The grant API needs the UUID of the role which we want to grant.

HTTP GET "/api/rbac/roles?name=rbac.global%23global%3AADMIN" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }`
=> status: 200 OK 
[ {
  "uuid" : "7ea7e6cb-16dd-4fb9-aa82-427df5f205d7", // globalAdminRoleUuidToGrant
  "object.uuid" : "6ec9aacc-2bd3-4678-a8f9-31e61eef3b0a",
  "objectTable" : "rbac.global",
  "objectIdName" : "global",
  "roleType" : "ADMIN",
  "roleName" : "rbac.global#6ec9aacc-2bd3-4678-a8f9-31e61eef3b0a:ADMIN",
  "roleIdName" : "rbac.global#global:ADMIN"
} ]

Grant the global ADMIN role to the API_KEY Subject

HTTP POST "/api/rbac/grants" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  -H 'Hostsharing-Assumed-Roles: rbac.global#global:ADMIN' \
  <<EOF
{
  "assumed" : true,
  "grantedRole.uuid" : "7ea7e6cb-16dd-4fb9-aa82-427df5f205d7", // globalAdminRoleUuidToGrant
  "granteeSubject.uuid" : "a91c0008-0000-0000-0000-000000000008"
}
EOF
=> status: 201 CREATED 7ea7e6cb-16dd-4fb9-aa82-427df5f205d7 // globalAdminRoleUuidToGrant

Verify the API-key can list the RBAC subjects of ALL realms via its global-admin role

HTTP GET "/api/rbac/subjects" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK 
[
  {
    "uuid" : "242a0005-0000-0000-0000-000000000005",
    "name" : "abc-peter.smith",
    "organization" : "abc",
    "type" : "USER"
  },
  {
    "uuid" : "27bf3e63-a89e-42b6-8881-42b192a6b2c4",
    "name" : "/abc-Team",
    "organization" : "abc",
    "type" : "GROUP"
  },
  {
    "uuid" : "a91c0005-0000-0000-0000-000000000005", // ApiKey-Subject: bootstrapped.key
    "name" : "bootstrapped.key",
    "organization" : "bootstrapped",
    "type" : "API_KEY"
  },
  {
    "uuid" : "a91c0008-0000-0000-0000-000000000008",
    "name" : "business.attempt.key",
    "organization" : "business",
    "type" : "API_KEY"
  },
  {
    "uuid" : "242a0006-0000-0000-0000-000000000006",
    "name" : "def-peter.smith",
    "organization" : "def",
    "type" : "USER"
  },
  "..."
]

Verify the API-key can fetch a single RBAC subject by its UUID

HTTP GET "/api/rbac/subjects/a91c0008-0000-0000-0000-000000000008" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK 
{
  "uuid" : "a91c0008-0000-0000-0000-000000000008",
  "name" : "business.attempt.key",
  "organization" : "business",
  "type" : "API_KEY"
}

Attempt to list memberships via the business API, authenticated just by the scoped API-key

HTTP GET "/api/hs/office/memberships" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 403 FORBIDDEN 
{
  "path" : "/api/hs/office/memberships",
  "statusCode" : 403,
  "statusPhrase" : "Forbidden",
  "message" : "ERROR: [403] API-key scopes do not allow GET /api/hs/office/memberships"
}

generated on 2026-08-10 01:38:08 for branch